package com.platform.modules.acme.utils;

import com.platform.common.utils.CertConstant;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.DecoderException;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemObjectGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.shredzone.acme4j.util.CSRBuilder;
import org.shredzone.acme4j.util.CertificateUtils;
import org.shredzone.acme4j.util.KeyPairUtils;

import javax.annotation.WillClose;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class AcmeUtil {

    /**
     * Loads a user key pair from {@value #}. If the file does not exist,
     * a new key pair is generated and saved.
     * <p>
     * Keep this key pair in a safe place! In a production environment, you will not be
     * able to access your account again if you should lose the key pair.
     *
     * @return User's {@link KeyPair}.
     */
    static public KeyPair loadOrCreateUserKeyPair(String fileName) throws IOException {
        File USER_KEY_FILE = new File(fileName);
        if (USER_KEY_FILE.exists()) {
            // If there is a key file, read it
            try (FileReader fr = new FileReader(USER_KEY_FILE)) {
                return KeyPairUtils.readKeyPair(fr);
            }

        } else {
            // If there is none, create a new key pair and save it
            KeyPair userKeyPair = KeyPairUtils.createKeyPair(2048);
            try (FileWriter fw = new FileWriter(USER_KEY_FILE)) {
                writeKeyPair(userKeyPair, fw);
            }
            return userKeyPair;
        }
    }

    public static void writeKeyPair(KeyPair keypair, @WillClose Writer w) throws IOException {
        try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
            jw.writeObject(keypair);
        }
    }


    /**
     * 创建CSR和私钥文件
     *
     * @param domains 域名
     * @return 返回PEM格式的csr
     * @throws IOException
     */
    public static String getCsrAndPrivateKey(Set<String> domains, String savePath) throws IOException {
        String domain = domains.stream().findFirst().orElseThrow(RuntimeException::new);
        KeyPair domainKeyPair = loadOrCreateDomainKeyPair(domain, savePath);
        return loadOrCreateDomainCertSignRequest(domains, domainKeyPair, savePath);
    }


    /**
     * 创建域名的密钥对，如果存在着加载以前的
     *
     * @param domain   域名，产生文件夹以及以域名命名私钥名，返回下载给用户
     * @param savePath 保存的父目录，子目录以域名为文件夹名称
     * @return
     * @throws IOException
     */
    protected static KeyPair loadOrCreateDomainKeyPair(String domain, String savePath) throws IOException {

        String fileName = "";
        if (domain.startsWith("*.")) {
            //通配符
            fileName = domain.replace("*", "_");
        } else {
            fileName = domain;
        }

//        String parentPath = savePath;
        File file = new File(savePath + File.separator + fileName);

        if (!file.exists())
            file.mkdirs();
        // 私钥文件
        File f = new File(file.getAbsolutePath() + File.separator + fileName + ".key");
        if (f.exists()) {
            try (FileReader fr = new FileReader(f)) {
                return KeyPairUtils.readKeyPair(fr);
            }
        } else {
            KeyPair domainKeyPair = KeyPairUtils.createKeyPair(2048);
            try (FileWriter fw = new FileWriter(f)) {
                KeyPairUtils.writeKeyPair(domainKeyPair, fw);
            }
            return domainKeyPair;
        }
    }


    /**
     * 返回PEM格式的csr，如果不存在则创建一个
     *
     * @param domains
     * @return
     * @throws IOException
     */
    static public String loadOrCreateDomainCertSignRequest(Set<String> domains, KeyPair domainKeyPair, String savePath) throws IOException {

        String fileName = domains.stream().findFirst().get();
        File folder = FileUtils.newFolder(savePath + File.separator + fileName);
        File csrFile = new File(folder.getAbsolutePath() + File.separator + fileName + ".csr");
        StringWriter sw = new StringWriter();
        PKCS10CertificationRequest pkcs10 = null;
        if (!csrFile.exists()) {
            CSRBuilder build = new CSRBuilder();
            build.addDomains(domains);
            build.setCountry("CN");
            build.sign(domainKeyPair);
            // CSR写成文件
            try (Writer out = new FileWriter(csrFile)) {
                build.write(out);
            }
            pkcs10 = build.getCSR();
        } else {
            pkcs10 = CertificateUtils.readCSR(new FileInputStream(csrFile));
        }
        //读取pkcs10
        try (PemWriter pw = new PemWriter(sw)) {
            pw.writeObject(new PemObject("CERTIFICATE REQUEST", pkcs10.getEncoded()));
        }

        return sw.toString();
    }


    /**
     * 检查是否CSR
     *
     * @param csrData
     * @return
     * @throws IOException
     */
    public static Boolean chkPkcs10(String csrData) {
        csrData = extracted(csrData);
        try {
            JcaPKCS10CertificationRequest req = new JcaPKCS10CertificationRequest(java.util.Base64.getDecoder().decode(csrData));
        } catch (IOException e) {
            log.error(e.getMessage());
            return false;
        }
        return true;
    }


    public static JcaPKCS10CertificationRequest getLetsEncryptCSR(String csrData) throws IOException {
        Security.addProvider(new BouncyCastleProvider());
        String csrStr = extracted(csrData);
        byte[] csrDer = Base64.decode(csrStr);
        JcaPKCS10CertificationRequest request = new JcaPKCS10CertificationRequest(csrDer);
        return request;
    }


    public static String extracted(String str) {
        if (str.indexOf(CertConstant.CERT_REQUEST) != -1) {
            str = str.replaceAll(CertConstant.CERT_REQUEST, "");
        }
        if (str.indexOf(CertConstant.END_REQUEST) != -1) {
            str = str.replaceAll(CertConstant.END_REQUEST, "");
        }
        if (str.indexOf(CertConstant.CERT_NEW_REQUEST) != -1) {
            str = str.replaceAll(CertConstant.CERT_NEW_REQUEST, "");
        }
        if (str.indexOf(CertConstant.END_NEW_REQUEST) != -1) {
            str = str.replaceAll(CertConstant.END_NEW_REQUEST, "");
        }
        if (str.indexOf(CertConstant.CERT_BEGIN) != -1) {
            str = str.replaceAll(CertConstant.CERT_BEGIN, "");
        }
        if (str.indexOf(CertConstant.CERT_END) != -1) {
            str = str.replaceAll(CertConstant.CERT_END, "");
        }
        if (str.indexOf(CertConstant.PFX_BEGIN) != -1) {
            str = str.replaceAll(CertConstant.PFX_BEGIN, "");
        }
        if (str.indexOf(CertConstant.PFX_END) != -1) {
            str = str.replaceAll(CertConstant.PFX_END, "");
        }
        str = replaceBlank(str);
        return str;
    }


    public static String replaceBlank(String str) {
        String dest = "";
        if (str!=null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }

        return dest;
    }

    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException, OperatorCreationException, PKCSException {
        String csr =
                "-----BEGIN CERTIFICATE REQUEST-----"
                        +"MIICaDCCAVACAQEwIzEUMBIGA1UEAwwLYXV0aHNzbC5jb20xCzAJBgNVBAYTAkNO"
                        +"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2CHW3hMn2SpKk6NO6aSY"
                        +"rL9UqSKXdxqr5SBwvDjARdUFoRAeU7FNBJsdU4hf6WF7EqM2Fwe5GQkVVnl/j6/M"
                        +"5X F0NnT/AbFzXsOEnCHrb9wVkQ13Hy6Fr1dNatGfl5B5KjSYGU kpneK94a98r1"
                        +"NPfBPP poyGWoIbzm 7LLHMW4NTIIZu3xR3q7vlDQ3kf/3dh3zJpb9owLbIEutP4"
                        +"VEt34WeRx0PDPelXSiDwyhukWmRosqXh5MsmWcnegmRo18N7rjuZiwtOPcybjSuO"
                        +"eYaGsRaBBY8jzqIKzRfyhgw66VL O/xkA05lNLDPaxi8IQTcNwbIdum M8PL0msi"
                        +"fQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAFGmvla6cFdTkWq iiTqKV1swEOt"
                        +"eOWKn0mZorFHRz4KP9ltUMMD10Z M5i8vnmM36OGY4/ub8wdRIYAabhmCvpelwsR"
                        +"Q/cmChnU4vvKOhKDn0SB0NP2uJhJtbUt6N1wHIG8cQFpoJ439Z8uawZw 207E1xe"
                        +"A2ljSDkZfE6aQju7MpNq0moIO9aQygPmYsV/qBa2/d9yr4BxuoJFUblMANclo1xZ"
                        +"OjtnvFgFwNYCRVdolMoEGOEL9ETguqguKoPwLnqIqji1PMXNskmzYVwd9/IX6eKE"
                        +"6692F4Re7EVy4ef32ABWFJAlXMZFcHU7uJwTCNMOza8A0fWWNFNx4/IvF5k="
                        +"-----END CERTIFICATE REQUEST-----";

//        Security.addProvider(new BouncyCastleProvider());
//        JcaPKCS10CertificationRequest req = new JcaPKCS10CertificationRequest(Base64.decode(csr));
////        PKCS10CertificationRequest req = new PKCS10CertificationRequest(Base64.decode(csr));
////        System.out.println(req.getSignatureAlgorithm().getAlgorithm().getId());
//        PublicKey publicKey = req.getPublicKey();
////        System.out.println(req.getPublicKey().getAlgorithm());
//        SubjectPublicKeyInfo pk = req.getSubjectPublicKeyInfo();
//
//        System.out.println(req.isSignatureValid(new JcaContentVerifierProviderBuilder().build(publicKey)));
//
//        System.out.println(getCertificateRequestFromPem(csr));
//        System.out.println(chkPkcs10(csr));

        loadOrCreateUserKeyPair("D:\\userkey.key");

    }

    public static PKCS10CertificationRequest getCertificateRequestFromPem(final String pemEncodedCsr) {
        if (pemEncodedCsr == null) {
            return null;
        }
        PKCS10CertificationRequest csr = null;
        final ByteArrayInputStream pemStream = new ByteArrayInputStream(pemEncodedCsr.getBytes(StandardCharsets.UTF_8));
        try {
            PEMParser pemParser = new PEMParser(new BufferedReader(new InputStreamReader(pemStream)));
            final Object parsedObj = pemParser.readObject();
            if (parsedObj instanceof PKCS10CertificationRequest) {
                csr = (PKCS10CertificationRequest) parsedObj;
            }
        } catch (IOException | DecoderException e) {//IOException that will be wrapped as (runtime) DecoderException
//            log.info("IOException while decoding certificate request from PEM: " + e.getMessage());
//            log.debug("IOException while decoding certificate request from PEM.", e);
            e.printStackTrace();
        }
        return csr;
    }
}
